/**
 * 
 */
package org.compiere.model;

import java.io.File;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.Properties;
import java.util.logging.Level;

import org.compiere.process.DocAction;
import org.compiere.process.DocumentEngine;
import org.compiere.util.CCache;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.joda.time.DateTime;

/**
 * @author Y.Ibrayev
 *
 */
public class MPrepaidExpenses extends X_FI_PrepaidExpenses implements DocAction {
	
	private static final long serialVersionUID = -4826485263091614133L;
	
	private static CCache<Integer,MPrepaidExpenses> s_cache	= new CCache<Integer,MPrepaidExpenses>("FI_PrepaidExpenses", 20, 2);	//	2 minutes

	private static final int CountMonthInYear = 12;

	private static final int MagicNumberForMonth = 1;

	public static final String DOCACTION_Prepare = "PR";
	public static final String DOCACTION_Complete = "CO";
	public static final String DOCACTION_Close = "CL";
	public static final String DOCACTION_None = "--";

	
	private ArrayList<X_FI_PrepaidExpensesLine> FI_PrepaidExpensesLines = null;

	public MPrepaidExpenses(Properties ctx) {
		super(ctx);
		// TODO Auto-generated constructor stub
	}

	public MPrepaidExpenses(Properties ctx, ResultSet rs,String trxName) {
		super(ctx,rs,trxName);
		// TODO Auto-generated constructor stub
	}
	
	public MPrepaidExpenses(Properties ctx, int FI_PrepaidExpenses_ID, String trxName) {
		super(ctx, FI_PrepaidExpenses_ID, trxName);
		// TODO Auto-generated constructor stub
	}
	
	public X_C_InvoiceLine getC_InvoiceLine() {
		X_C_InvoiceLine result = null;
	    PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			String query = "SELECT * FROM C_InvoiceLine "
		                 + "WHERE IsActive = 'Y' "
					     + "and FI_PrepaidExpenses_ID = ? " 
		                 + "and AD_Client_ID = ? "
					     + "and c_invoice_id in "
		                 + "(select c_invoice_id from c_invoice "
					     + " where c_invoice.isactive='Y' "
		                 + "   and c_invoice.c_invoice_id = c_invoiceline.c_invoice_id "
					     + "   and c_invoice.DocStatus='"+MInvoice.DOCSTATUS_Completed+"')";
			pstmt = DB.prepareStatement(query,null);
			pstmt.setInt(1, getFI_PrepaidExpenses_ID());
			pstmt.setInt(2, getAD_Client_ID());
			rs = pstmt.executeQuery();	
			if(rs.next()) {
				result = new X_C_InvoiceLine(getCtx(),rs,null);
			}
		} 
		catch (SQLException e) {
			log.log(Level.SEVERE, "product", e);
			return null;
		} 
		finally {
			DB.close(rs, pstmt);
			rs = null; 
			pstmt = null;
		}	
		return result;
	}
	
	public BigDecimal getMonthAmount(Date currentDate) {
		BigDecimal result = null;
		try {
			CalculateCFP calc = new CalculateCFP(getAmount().doubleValue(), new Date(getDateFrom().getTime()), new Date(getDateTo().getTime()),currentDate);
			result = calc.getAmountMonth();
			result = result.setScale(2,BigDecimal.ROUND_HALF_UP); 
		}
		catch (Exception e) {
			log.log(Level.SEVERE, "MPrepaidExpenses: division by zerro");
		}
		return result;
	}

	public int getCountMonth() {
		int result = 0;
		DateTime fromDate = new DateTime(getDateFrom().getTime());
		DateTime toDate = new DateTime(getDateTo().getTime());
		int fromYear = fromDate.getYear();
		int toYear = toDate.getYear();
		int fromMonth = fromDate.getMonthOfYear();
		int toMonth = toDate.getMonthOfYear();
		
		int aYear = toYear - fromYear;
		result = aYear * CountMonthInYear - fromMonth + toMonth + MagicNumberForMonth;
		
		return result;
	}
	
	public MAccount getChargeAccount() {
		MAccount result = null;
		String sql = "SELECT C_ElementValue_ID FROM C_Charge WHERE C_Charge_ID = ?";
		int Account_ID = DB.getSQLValue(get_TrxName(), sql, getC_Charge_ID());
		if (Account_ID > 0) {
			sql = "SELECT c_validcombination_id FROM c_validcombination WHERE C_BPartner_ID is null and isActive='Y' AND account_id = " + Account_ID + " and ad_org_id = " + getAD_Org_ID() + " AND AD_Client_ID=" + getAD_Client_ID();
			int ValidCombination_ID = DB.getSQLValue(get_TrxName(), sql);
			result =  MAccount.get(getCtx(),ValidCombination_ID);
		}
		return result;
	}
	
	public Boolean isPeriodClosed(MPeriod period) {
		Boolean result = false;
		result = isPeriodClosed(period.getC_Period_ID());
		return result;
	}
	
	public Boolean isPeriodClosed(int C_Period_ID) {
		Boolean result = false;
		result = getGL_Journal(C_Period_ID) != null; 
		return result;
	}
	
	public X_GL_Journal getGL_Journal(int C_Period_ID) {
		X_GL_Journal result = null;
		for(X_FI_PrepaidExpensesLine item: getFI_PrepaidExpensesLines()) {
			if (item.getC_Period_ID() == C_Period_ID && item.isActive()) {
				int GL_Journal_ID = item.getGL_Journal_ID();
				result = new X_GL_Journal(getCtx(),GL_Journal_ID,null);
				break;
			}
		}
		return result;
	}
	
	public void setGL_Journal(X_GL_Journal journal, int C_Period_ID) {
		if (journal != null) {
			int journalID = journal.getGL_Journal_ID();
			if (journalID > 0 && getGL_Journal(C_Period_ID) == null) {
				int FI_PrepaidExpenses_ID = getFI_PrepaidExpenses_ID();
				if (FI_PrepaidExpenses_ID > 0) {
					addGL_Journal(journal, C_Period_ID);
				}
			}
			else if (journal.getGL_Journal_ID() != getGL_Journal(C_Period_ID).getGL_Journal_ID()) {
				editGL_Journal(journal, C_Period_ID);
			}
		}
		return ;
	}
	
	private void addGL_Journal(X_GL_Journal journal, int C_Period_ID) {
		setFI_PrepaidExpensesLine(journal.getGL_Journal_ID(), C_Period_ID);
		return ;
	}

	private void editGL_Journal(X_GL_Journal journal, int C_Period_ID) {
		X_GL_Journal oldGL_Journal = getGL_Journal(C_Period_ID);
		if (oldGL_Journal != null) {
			int oldGL_Journal_ID = oldGL_Journal.getGL_Journal_ID();
			X_FI_PrepaidExpensesLine line = getFI_PrepaidExpensesLine(oldGL_Journal_ID,C_Period_ID);
			line.setIsActive(false);
			if (!line.save()) {
				log.log(Level.WARNING, "Can't make an active - " + line.getFI_PrepaidExpensesLine_ID());
			}
		}
		addGL_Journal(journal, C_Period_ID);
		return ;
	}
	
	public ArrayList<X_FI_PrepaidExpensesLine> getFI_PrepaidExpensesLines() {
		if (FI_PrepaidExpensesLines == null) {
			LoadLines();
		}
		return FI_PrepaidExpensesLines;
	}
	
	private void LoadLines() {
		FI_PrepaidExpensesLines = null;
	    PreparedStatement pstmt = null;
		ResultSet rs = null;
		if (getFI_PrepaidExpenses_ID() < 1) {
			return;
		}
		try {
			FI_PrepaidExpensesLines = new ArrayList<X_FI_PrepaidExpensesLine>();
			String query = "SELECT * FROM FI_PrepaidExpensesLine WHERE FI_PrepaidExpenses_ID = ? and AD_Client_ID = ?";
			pstmt = DB.prepareStatement(query,null);
			pstmt.setInt(1, getFI_PrepaidExpenses_ID());
			pstmt.setInt(2, getAD_Client_ID());
			rs = pstmt.executeQuery();	
			while (rs.next()) {
				FI_PrepaidExpensesLines.add(new X_FI_PrepaidExpensesLine(getCtx(),rs, null));
			}
		} 
		catch (SQLException e) {
			log.log(Level.SEVERE, "product", e);
			FI_PrepaidExpensesLines = null;
			return;
		} 
		finally {
			DB.close(rs, pstmt);
			rs = null; 
			pstmt = null;
		}	
		return;
	}
	
	public X_FI_PrepaidExpensesLine setFI_PrepaidExpensesLine(X_GL_Journal journal) {
		X_FI_PrepaidExpensesLine result = setFI_PrepaidExpensesLine(journal.getGL_Journal_ID(), journal.getC_Period_ID());
		return result;
	}
	
	public X_FI_PrepaidExpensesLine setFI_PrepaidExpensesLine(int GL_Journal_ID,int C_Period_ID) {
		X_FI_PrepaidExpensesLine result = null;
		X_FI_PrepaidExpensesLine item = getFI_PrepaidExpensesLine(GL_Journal_ID,C_Period_ID);
		if (item != null) {
			item.setIsActive(false);
			if (!item.save()) {
				log.log(Level.WARNING, "Can't make an active - " + item.getFI_PrepaidExpensesLine_ID());
				return result;
			}
		}
		result = new X_FI_PrepaidExpensesLine(getCtx());
		
		result.setFI_PrepaidExpenses_ID(getFI_PrepaidExpenses_ID());
		result.setAD_Client_ID(getAD_Client_ID());
		result.setAD_Org_ID(getAD_Org_ID());
		result.setGL_Journal_ID(GL_Journal_ID);
		result.setC_Period_ID(C_Period_ID);
		result.setIsActive(true);
		if (result.save()) {
			LoadLines();
		}
		else {
			log.log(Level.WARNING, "Can't save - " + result.getFI_PrepaidExpensesLine_ID());
			result = null;
		}
		return result;
	}
	
	public  X_FI_PrepaidExpensesLine getFI_PrepaidExpensesLine(X_GL_Journal journal) {
		X_FI_PrepaidExpensesLine result = getFI_PrepaidExpensesLine(journal.getGL_Journal_ID(), journal.getC_Period_ID());
		return result;
	}
	
	public X_FI_PrepaidExpensesLine getFI_PrepaidExpensesLine(int GL_Journal_ID,int C_Period_ID) {
		X_FI_PrepaidExpensesLine result = null;
		
		if (FI_PrepaidExpensesLines == null) {
			LoadLines();
			if (FI_PrepaidExpensesLines == null) {
				return result;
			}
		}
		
		for(X_FI_PrepaidExpensesLine item: FI_PrepaidExpensesLines) {
			if (item.getGL_Journal_ID() == GL_Journal_ID && item.getC_Period_ID() == C_Period_ID && item.isActive() ) {
				result = item;
				break;
			}
		}
		
		return result;
	}

	/**	Process Message 			*/
	private String		m_processMsg = null;
	/**	Just Prepared Flag			*/
	private boolean		m_justPrepared = false;
	
	@Override
	public boolean processIt(String action) throws Exception {
		m_processMsg = null;
		DocumentEngine engine = new DocumentEngine (this, getDocStatus());
		return engine.processIt (action, getDocAction());
	}

	@Override
	public boolean unlockIt() {
		log.info("unlockIt - " + toString());
		setProcessing(false);
		return true;
	}

	@Override
	public boolean invalidateIt() {
		log.info("invalidateIt - " + toString());
		setDocAction(DOCACTION_Prepare);
		return true;
	}

	@Override
	public String prepareIt() {
		log.info(toString());
		m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE);
		if (m_processMsg != null)
			return DocAction.STATUS_Invalid;

		m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE);
		if (m_processMsg != null)
			return DocAction.STATUS_Invalid;

		//	Add up Amounts
		m_justPrepared = true;
		if (!DOCACTION_Complete.equals(getDocAction()))
			setDocAction(DOCACTION_Complete);
		
		return DocAction.STATUS_InProgress;
	}

	@Override
	public boolean approveIt() {
		log.info(toString());
		return true;
	}

	@Override
	public boolean rejectIt() {
		log.info(toString());
		return true;
	}

	@Override
	public String completeIt() {
		//	Re-Check
		if (!m_justPrepared)
		{
			String status = prepareIt();
			if (!DocAction.STATUS_InProgress.equals(status))
				return status;
		}

		m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE);
		if (m_processMsg != null)
			return DocAction.STATUS_Invalid;

		//setDocAction(DOCACTION_Close);
		
		setProcessed(true);
		setDateAcct(new Timestamp(System.currentTimeMillis()));
		return DocAction.STATUS_Completed;
	}

	@Override
	public boolean voidIt() {
		log.info(toString());
		// Before Void
		m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID);
		if (m_processMsg != null)
			return false;

		if (DOCSTATUS_Closed.equals(getDocStatus())
			|| DOCSTATUS_Reversed.equals(getDocStatus())
			|| DOCSTATUS_Voided.equals(getDocStatus()))
		{
			m_processMsg = "Document Closed: " + getDocStatus();
			setDocAction(DOCACTION_None);
			return false;
		}

		// After Void
		m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID);
		if (m_processMsg != null)
			return false;

		setProcessed(true);
		setDocAction(DOCACTION_None);
		return true;
	}

	@Override
	public boolean closeIt() {
		log.info(toString());
		// Before Close
		m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE);
		if (m_processMsg != null)
			return false;

		setProcessed(true);
		setDocAction(DOCACTION_None);

		// After Close
		m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE);
		if (m_processMsg != null)
			return false;
		return true;
	}

	@Override
	public boolean reverseCorrectIt() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean reverseAccrualIt() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean reActivateIt() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public String getSummary() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getDocumentInfo() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public File createPDF() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getProcessMsg() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int getDoc_User_ID() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int getC_Currency_ID() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public BigDecimal getApprovalAmt() {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	public Timestamp getDateAcct() {
		Timestamp result = super.getDateAcct();
		if (result == null) {
			result = new Timestamp(System.currentTimeMillis());
			setDateAcct(result);
		}
		return result;
	}
	
	
}
